/**
 * <vendor>
 *  Copyright 2009 Quanben Tech.
 * </vendor>
 */

using System.Text;

namespace QSharp.String.Compiler
{
    public static class Lexical
    {
        public class CharSet : Utility.Set<char>
        {
            public CharSet(params char[] initialChars)
                : base(initialChars)
            {
            }

            public CharSet CreateAsAlphabet()
            {
                myList.Clear();
                for (char ch = 'A'; ch <= 'Z'; ch++)
                {
                    myList.Add(ch);
                }
                for (char ch = 'a'; ch <= 'z'; ch++)
                {
                    myList.Add(ch);
                }
                return this;
            }

            public CharSet CreateAsDigitSet()
            {
                for (char ch = '0'; ch <= '9'; ch++)
                {
                    myList.Add(ch);
                }
                return this;
            }

            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                foreach (char ch in myList)
                {
                    sb.Append(ch);
                }
                return sb.ToString();
            }
        }

        public static bool IsBlank(char ch)
        {
            return (ch == ' ' || ch == '\t');
        }

        public static bool IsLetter(char ch)
        {
            return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
        }

        public static bool IsDigit(char ch)
        {
            return (ch >= '0' && ch <= '9');
        }

        public static int ReadHex(out int val, ITokenStream stream, int nMaxToRead)
        {
            val = 0;
            int nRead;
            for (nRead = 0; nMaxToRead <= 0 || nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }

                char ch = token;
                if (IsDigit(ch))
                {
                    val *= 16;
                    val += ch - '0';
                }
                else if (ch >= 'A' && ch <= 'F')
                {
                    val *= 16;
                    val += ch - 'A' + 10;
                }
                else if (ch >= 'a' && ch <= 'f')
                {
                    val *= 16;
                    val += ch - 'a' + 10;
                }
                else
                {
                    break;
                }
                stream.Move(1);
            }
            return nRead;
        }

        public static int ReadHex(out int val, ITokenStream stream)
        {
            return ReadHex(out val, stream, 0);
        }

        public static int ReadDec(out int val, ITokenStream stream, int nMaxToRead)
        {
            val = 0;
            int nRead;
            for (nRead = 0; nMaxToRead <= 0 || nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }

                char ch = token;
                if (IsDigit(ch))
                {
                    val *= 10;
                    val += ch - '0';
                }
                else
                {
                    break;
                }
                stream.Move(1);
            }
            return nRead;
        }

        public static int ReadDec(out int val, ITokenStream stream)
        {
            return ReadDec(out val, stream, 0);
        }

        public static int ReadDec(out uint val, ITokenStream stream)
        {
            int ival;
            int res = ReadDec(out ival, stream, 0);
            val = (uint)ival;
            return res;
        }

        public static int SkipBlanks(ITokenStream stream, int nMaxToRead)
        {
            int nRead;
            for (nRead = 0; nMaxToRead <= 0 || nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }

                char ch = token;
                if (!IsBlank(ch))
                {
                    break;
                }

                stream.Move(1);
            }
            return nRead;
        }

        public static int SkipBlanks(ITokenStream stream)
        {
            return SkipBlanks(stream, 0);
        }

        public static int ReadIdentifier(out string val, ITokenStream stream, int nMaxToRead)
        {
            val = "";
            int nRead;
            for (nRead = 0; nMaxToRead <= 0 || nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }

                char ch = token;
                if (IsLetter(ch) || IsDigit(ch) && val != "")
                {
                    val += ch;
                }
                else
                {
                    break;
                }

                stream.Move(1);
            }
            return nRead;
        }

        public static int ReadIdentifier(out string val, ITokenStream stream)
        {
            return ReadIdentifier(out val, stream, 0);
        }

        public static int ReadUntil(out string val, ITokenStream stream, int nMaxToRead, CharSet cs)
        {
            StringBuilder sb = new StringBuilder();
            int nRead;
            for (nRead = 0; nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }
                
                char ch = token;
                if (cs.IsContaining(ch))
                {
                    break;
                }
                sb.Append(ch);
                stream.Move(1);
            }
            val = sb.ToString();
            return nRead;
        }

        public static int ReadUntil(out string val, ITokenStream stream, CharSet cs)
        {
            return ReadUntil(out val, stream, int.MaxValue, cs);
        }

        public static int SkipUntil(ITokenStream stream, int nMaxToRead, CharSet cs)
        {
            int nRead;
            for (nRead = 0; nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }

                char ch = token;
                if (cs.IsContaining(ch))
                {
                    break;
                }
                stream.Move(1);
            }
            return nRead;
        }

        public static int SkipUntil(ITokenStream stream, CharSet cs)
        {
            return SkipUntil(stream, int.MaxValue, cs);
        }

        public static int ReadWhen(ref string val, ITokenStream stream, int nMaxToRead, CharSet cs)
        {
            if (val != null)
            {
                val = "";
            }
            int nRead;
            for (nRead = 0; nMaxToRead <= 0 || nRead < nMaxToRead; nRead++)
            {
                CharToken token = (CharToken)stream.Read();
                if (token == null)
                {
                    break;
                }
                
                char ch = token;
                if (!cs.IsContaining(ch))
                {
                    break;
                }
                if (val != null)
                {
                    val += ch;
                }
                stream.Move(1);
            }
            return nRead;
        }

        public static int ReadWhen(ref string val, ITokenStream stream, CharSet cs)
        {
            return ReadWhen(ref val, stream, 0, cs);
        }

        public static int SkipWhen(ITokenStream stream, int nMaxToRead, CharSet cs)
        {
            string dummy = null;
            return ReadWhen(ref dummy, stream, nMaxToRead, cs);
        }

        public static int SkipWhen(ITokenStream stream, CharSet cs)
        {
            return SkipWhen(stream, 0, cs);
        }
    }

#if TEST_String_Compiler_Lexical
    public class Lexical_Test
    {
        public static void Main(string[] args)
        {
            string s = "This is a string for test][,sfd";
            StringStream ss = new StringStream(s);

            Lexical.CharSet cs = new Lexical.CharSet();
            cs.CreateAsAlphabet();
            cs.Unionize((new Lexical.CharSet()).CreateAsDigitSet().Add(' '));

            string val = "";
            int nRead = Lexical.ReadWhen(ref val, ss, 0, cs);
            Console.WriteLine("{0} characters has successfully been read, the result string is \"{1}\".", nRead, val);
        }
    }
#endif

}   /* namespace QSharp.String.Compiler */

